namespace egret.socketio {


    interface EmitterEventBin {
        event: string;
        listion: Function;
        listenerContext: any;
        dispatchOnce: boolean;
    }

    /** 
     * class Emitter
     */

    export class Emitter {
        protected callbacks: any = {};

        public on<Z>(event: string, fn: Function, listenerContext?: Z): Emitter {
            this.$addListener(event, fn, listenerContext);
            return this;
        }

        public once<Z>(event: string, fn: Function, listenerContext?: Z): Emitter {
            this.$addListener(event, fn, listenerContext, true);
            return this;
        }

        public off(event?: string, fn?: Function, listenerContext?: any): Emitter {
            this.$removeListener(event, fn, listenerContext);
            return this;
        }

        public emit(event: string, ...args) {
            let callback: Array<EmitterEventBin> = this.callbacks[event];
            if (typeof callback === 'undefined' || callback === null)
                return;
            let onceList = [];
            callback.forEach(element => {
                element.listion.call(element.listenerContext, ...args);
                if (element.dispatchOnce) {
                    onceList.push(element);
                }
            });

            while (onceList.length) {
                let eventBin = onceList.pop();
                this.$removeListener(eventBin.event, eventBin.listener, eventBin.listenerContext);
            }
        }


        $addListener(event: string, fn: Function, listenerContext?: any, dispatchOnce?: boolean): void {
            let callback: Array<EmitterEventBin> = this.callbacks[event];

            if (!callback) {
                callback = [];
                this.callbacks[event] = callback;
            }
            let data: EmitterEventBin = { listion: fn, listenerContext, event, dispatchOnce: !!dispatchOnce };
            callback.push(data);
        }

        $removeListener(event?: string, fn?: Function, listenerContext?: any): void {
            if (!event) {
                this.callbacks = [];
                return;
            }

            let callback = this.callbacks[event];
            if (callback) {
                this.$removeEventBin(callback, fn, listenerContext);
                if (callback.length == 0) {
                    this.callbacks[event] = null;
                }
            }
        }

        $removeEventBin(list: any[], listener?: Function, thisObject?: any): void {
            if (listener && thisObject) {
                for (let i = 0; i < list.length; i++) {
                    let bin = list[i];
                    if (bin.listener == listener && bin.listenerContext == thisObject) {
                        list.splice(i, 1);
                    }
                }
            } else {
                for (let i = 0; i < list.length; i++) {
                    list.splice(i, 1);
                }
            }
        }
    }
}